﻿/*
 * Author: Mattan360
 * Contributors:  VillageIdiot
 */

using System;
using System.Runtime.InteropServices;
using Irrlicht.Net.Core;
using Irrlicht.Net.GUI;
using Irrlicht.Net.Scene;
using Irrlicht.Net.Video;
using Irrlicht.Net.IO;

namespace Irrlicht.Net
{
    public class Device : ReferenceCounted
    {
        #region PInvoke
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr CreateNewInstance(uint deviceType, uint width, uint height, uint bits,
            bool fullscreen, bool stencilBuffer, bool vSync);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getVideoDriver(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getSceneManager(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getGUIEnvironment(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getOSOperator(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getFileSystem(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getCursorControl(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getTimer(IntPtr device);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_setWindowCaption(IntPtr device,
            [MarshalAs(UnmanagedType.BStr)]
            string text);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_setResizeable(IntPtr device, bool isResizeable);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_setGammaRamp(IntPtr device, float r, float g, float b, float brightness, float contrast);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern uint Device_getColorFormat(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_getVersion(IntPtr device);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Device_run(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_yield(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_sleep(IntPtr device, uint time, bool pauseTimer);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Device_isWindowActive(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Device_isWindowFocused(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Device_isWindowMinimized(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern bool Device_isFullscreen(IntPtr device);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_minimizeWindow(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_maximizeWindow(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_restoreWindow(IntPtr device);

        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern IntPtr Device_GetEventReceiver(IntPtr device);
        [DllImport(@"IrrlichtW.dll", CallingConvention = CallingConvention.Cdecl)]
        internal static extern void Device_SetEventReceiver(IntPtr device, IntPtr receiver);
        #endregion

        internal VideoDriver driver;
        internal SceneManager manager;
        internal GUIEnvironment environment;
        internal OSOperator os;
        internal FileSystem fileSystem;
        internal CursorControl cursorControl;
        internal Timer timer;
        internal string caption;

        public Device()
            : this(DriverTypes.Software, new Dimension2D(640, 480), 16, false, false, false)
        { }

        /// <summary>
        /// Creates a new Irrlicht Device
        /// </summary>
        /// <param name="driverType">The renderer of the driver</param>
        /// <param name="dimensions">The size of the window</param>
        /// <param name="fullscreen">True if you want the window to be fullscreen</param>
        public Device(DriverTypes driverType, Dimension2D dimensions, bool fullscreen)
            : this(driverType, dimensions, 16, fullscreen, false, false)
        {}

        public Device(DriverTypes driverType)
            : this(driverType, new Dimension2D(640, 480), 16, false, false, false)
        { }

        public Device(DriverTypes driverType, Dimension2D dimensions)
            : this(driverType, dimensions, 16, false, false, false)
        { }

        public Device(DriverTypes driverType, Dimension2D dimensions, uint bits)
            : this(driverType, dimensions, bits, false, false, false)
        { }

        public Device(DriverTypes driverType, Dimension2D dimensions, uint bits, bool fullscreen)
            : this(driverType, dimensions, bits, fullscreen, false, false)
        { }

        public Device(DriverTypes driverType, Dimension2D dimensions, uint bits, bool fullscreen, bool stencilBuffer)
            : this(driverType, dimensions, bits, fullscreen, stencilBuffer, false)
        { }

        /// <summary>
        /// Creates a new Irrlicht Device
        /// </summary>
        /// <param name="driverType">The renderer of the driver</param>
        /// <param name="dimensions">The size of the window</param>
        /// <param name="fullscreen">True if you want the window to be fullscreen</param>
        /// <param name="bits">Bits per pixel in fullscreen mode. Ignored if windowed mode.</param>
        /// <param name="stencilBuffer">Specifies if the stencil buffer should be enabled. Set this to true,
        /// if you want the engine be able to draw stencil buffer shadows. Note that not all
        /// devices are able to use the stencil buffer. If they don't no shadows will be drawn.</param>
        /// <param name="vSync">Specifies vertical syncronisation: If set to true, the driver will wait
        /// for the vertical retrace period, otherwise not.</param>
        public Device(DriverTypes driverType, Dimension2D dimensions, uint bits, bool fullscreen, bool stencilBuffer, bool vSync)
        {
            pointer = CreateNewInstance((uint)driverType, dimensions.Width, dimensions.Height, bits, fullscreen, stencilBuffer, vSync);

            driver = VideoDriver.CreateVideoDriver(Device_getVideoDriver(pointer));
            manager = SceneManager.CreateSceneManager(Device_getSceneManager(pointer));
            environment = GUIEnvironment.CreateGUIEnvironment(Device_getGUIEnvironment(pointer));
            fileSystem = FileSystem.CreateFileSystem(Device_getFileSystem(pointer));
            timer = Timer.CreateTimer(Device_getTimer(pointer));
            cursorControl = CursorControl.CreateCursorControl(Device_getCursorControl(pointer));
            os = new OSOperator(Device_getOSOperator(pointer));
        }

        /// <summary>
        /// Gets and sets the current VideoDriver
        /// </summary>
        public VideoDriver VideoDriver { get { return driver; } set { driver = value; } }
        
        /// <summary>
        /// Gets and sets the current SceneManager
        /// </summary>
        public SceneManager SceneManager { get { return manager; } set { manager = value; } }
        
        /// <summary>
        /// Gets and sets the current GUIEnvironment
        /// </summary>        
        public GUIEnvironment GUIEnvironment { get { return environment; } set { environment = value; } }
        
        /// <summary>
        /// Gets or sets the curent FileSystem
        /// </summary>        
        public FileSystem FileSystem { get { return fileSystem; } set { fileSystem = value; } }

        /// <summary>
        /// Gets or sets the current CursorControl
        /// </summary>        
        public CursorControl CursorControl { get { return cursorControl; } set { cursorControl = value; } }

        /// <summary>
        /// Gets or sets the current Timer.
        /// </summary>
        public Timer Timer { get { return timer; } set { timer = value; } }

        /// <summary>
        /// 
        /// </summary>        
        public OSOperator OSOperator { get { return os; } set { os = value; } }
        
        /// <summary>
        /// Gets and sets the current Window Caption
        /// </summary>
        public string WindowCaption
        {
            get
            {
                return caption;
            }

            set
            {
                string capt = value;
                Device_setWindowCaption(pointer, capt);
                caption = capt;
            }
        }

        public IEventReceiver EventReceiver
        {        
            get
            {
                return (IEventReceiver) Marshal.GetObjectForIUnknown(Device_GetEventReceiver(pointer));
            }
        
            set
            {
                Device_SetEventReceiver(pointer, Marshal.GetIUnknownForObject(value));    
            }
        }

        public IntPtr EventReceiverPtr
        {
            get
            {
                return Device_GetEventReceiver(pointer);
            }
        }

        /// <summary>
        /// Gets the current Color Format
        /// </summary>
        public ColorFormat ColorFormat
        {
            get
            {
                return (ColorFormat)Device_getColorFormat(pointer);
            }
        }

        /// <summary>
        /// Checks if the current window is active, or not
        /// </summary>
        public bool IsWindowActive
        {
            get
            {
                return Device_isWindowActive(pointer);
            }
        }

        /// <summary>
        /// Checks if the current window is focused, or not
        /// </summary>
        public bool IsWindowFocused
        {
            get
            {
                return Device_isWindowFocused(pointer);
            }
        }

        /// <summary>
        /// Checks if the current window is minimized, or not
        /// </summary>
        public bool IsWindowMinimized
        {
            get
            {
                return Device_isWindowMinimized(pointer);
            }
        }

        /// <summary>
        /// Checks whenever the current window is fullscreen, or not
        /// </summary>
        public bool IsFullscreen
        {
            get
            {
                return Device_isFullscreen(pointer);
            }
        }

        /// <summary>
        /// Sets a value that indicates if te window may be resized
        /// </summary>
        public bool AllowResizing
        {
            set
            {
                Device_setResizeable(pointer, value);
            }
        }

        /// <summary>
        /// Gets the version of the current Irrlicht assembly
        /// </summary>
        public Version Version
        {
            get
            {
                string version = Marshal.PtrToStringAnsi(Device_getVersion(pointer));
                return new Version(version);
            }
        }

        /// <summary>
        /// Sets the current gamma ramp
        /// (Getting should be implanted)
        /// </summary>
        public GammaRamp GammaRamp
        {
            set
            {
                GammaRamp ramp = value;
                Device_setGammaRamp(pointer, ramp.r, ramp.g, ramp.b, ramp.brightness, ramp.contrast);
            }
        }

        public bool Run()
        {
            return Device_run(pointer);
        }

        public void Yield()
        {
            Device_yield(pointer);
        }

        public void Sleep(uint time)
        {
            Device_sleep(pointer, time, false);
        }

        public void Sleep(uint time, bool pauseTimer)
        {
            Device_sleep(pointer, time, pauseTimer);
        }

        /// <summary>
        /// Minimizes the window, if possible
        /// </summary>
        public void MinimizeWindow()
        {
            Device_minimizeWindow(pointer);
        }

        /// <summary>
        /// Maximizes the window, if possible
        /// </summary>
        public void MaximizeWindow()
        {
            Device_maximizeWindow(pointer);
        }

        /// <summary>
        /// Restores the window, if possible
        /// </summary>
        public void RestoreWindow()
        {
            Device_restoreWindow(pointer);
        }
    }
}
